﻿using Core.Framework.Model.Common;
using Core.Framework.Model.WebSockets;
using Core.Framework.Redis;
using Core.Framework.Redis.Queue_Helper;
using Core.Framework.Util;
using Core.IBusiness.ISocketModule;
using Core.Service.TaskHandle;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Core.Framework.Loger;
using System.IO;
using System.Diagnostics;
using Core.IBusiness.ILoggerModule;
using Microsoft.AspNetCore.Mvc.Formatters;
using System.Text;

namespace Core.Middleware.WebSockets
{
    public class WebSokcetHelper
    {

        /// <summary>
        /// 中间键
        /// </summary>
        public class WebSocketMidWare
        {
            private readonly RequestDelegate requestDelegate;

            /// <summary>
            /// 事件模板用名
            /// </summary>
            //public const string Template = "WebSocket";

            /// <summary>
            /// 锁当前会话
            /// </summary>
            public static object lock_Subscription = new object();

            /// <summary>
            /// 用户退出
            /// </summary>
            private static bool OutClientFlag = false;

            /// <summary>
            /// 日志接口
            /// </summary>
            private ILog iLog = BuildServiceProvider.GetService<ILog>();

            public WebSocketMidWare(RequestDelegate requestDelegate)
            {
                this.requestDelegate = requestDelegate;
                Task.Run(() => { InitServiceClinetParameter(1000);  });
            }

            public async Task Invoke(HttpContext context)
            {
                if (context.Request.Path == "/v1/.ws" && context.WebSockets.IsWebSocketRequest)
                {

                    WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync();

                    CancellationToken cancellationToken = context.RequestAborted;

                    await WebSocketApplication
                        .RecvAsync(
                            currentSocket,
                            cancellationToken,
                            WebSocketApplication.MessageBranchAction,
                            this.GetClientInfoByUserToken,
                            this.WsOutLogin);
                }
                else if (context.Request.Path == "/")
                {
                    await context.Response.WriteAsync("hello there !");
                }
                else
                {
                    var watch = new Stopwatch();
                    watch.Start();

                    context.Response.OnStarting(() =>
                    {

                        watch.Stop();

                        // 响应时间
                        context.Response.Headers["Request_Time"] = watch.ElapsedMilliseconds.ToString()+ "ms";

                        // add log
                        //iLog.Request<WebSocketMidWare>(
                        //    value: new
                        //    {
                        //        Host = context.Request.Host.ToString(),
                        //        Path = context.Request.Path,
                        //        Query = context.Request.QueryString.ToString(),
                        //        Body = new StreamReader(context.Request.Body).ReadToEnd(),
                        //        Time = watch.ElapsedMilliseconds
                        //    }.TryToJson(),
                        //    tag: context.Request.Method
                        //);

                        return Task.CompletedTask;

                    });

                    await requestDelegate(context);
                }
            }

            /// <summary>
            /// 根据用户 token 获取用户信息
            /// </summary>
            Func<string, WebSocket, Tuple<ClientInfo, bool>> GetClientInfoByUserToken = (token, webSocket) =>
            {
                // 根据token 获取登陆信息
                var result
                    = RedisQueueHelper.HashGet(RedisQueueHelperParameter.WebSocketByToken, token);

                // 判断是否登陆
                if (!string.IsNullOrWhiteSpace(result))
                {
                    var client = ((string)result).TryToEntity<ClientInfo>();
                    if (client != null)
                    {
                        // 通知客户端登录成功
                        WebSocketApplication.SendAsync(webSocket, new { type = "userlogin", date = 200 });

                        ILog iLog = BuildServiceProvider.GetService<ILog>();

                        Task.Run(() =>
                        {
                            Thread.Sleep(3000);
                            #region 离线消息 Offline

                            // 获取服务
                            var service = BuildServiceProvider.GetService<ISocketMessage>();

                            // Redis离线消息
                            var singleNulls
                                = RedisQueueHelper.GetListPopByLength(RedisQueueHelperParameter.SingleNull, 0, 100, force: true);

                            // 查询离线消息
                            var list = service.GetOfflineMessagesByMsgKeyAndTemplate(
                                client.User.Subscription,
                                client.Template,
                                client.Project.ProjectToken,
                                new Pagination
                                {
                                    page = 1,
                                    rows = 200
                                });

                            List<string> offline = new List<string>();


                            // 构造消息
                            Func<MessageQueue, string> StructureParameter = (messageQueue) =>
                            {

                                iLog.Queue<WebSokcetHelper>(new
                                {
                                    Client = CoreStartupHelper.GetConfigValue("Service"),
                                    TaskUserKey = client.User.Key
                                }.TryToJson(), "offline", messageQueue.Message.Token);

                                return new
                                {
                                    Token = messageQueue.Message.Token,
                                    message = messageQueue.Message.Content,
                                    messageParameter = messageQueue.Message.Parameter,

                                    userKey = messageQueue.ClientInfo.User.Key,
                                    MessageKey = messageQueue.Message.MessageKey,
                                    userParameter = messageQueue.ClientInfo.User.Parameter,
                                    sendDateTime = messageQueue.Message.SendDateTime,
                                    template = messageQueue.Template,

                                    messageType = messageQueue.Message.MessageType.ToString(),

                                }.TryToJson();
                            };

                            // sql 查询的消息
                            foreach (var item in list)
                                offline.Add(StructureParameter(item.MsgContext.TryToEntity<MessageQueue>()));

                            // redis 查询的消息
                            foreach (var item in singleNulls)
                            {
                                var val = ((string)item);

                                if (val.Contains(client.Project.ProjectToken))
                                {
                                    var model = val.TryToEntity<MessageQueue>();

                                    if (null != model)
                                    {
                                        if (null != client.Project && model.Template == client.Template)
                                        {
                                            if (
                                                client.Project.ProjectToken == model.ClientInfo.Project.ProjectToken
                                                && client.User.Subscription.Contains(model.Message.MessageKey)
                                            )
                                                offline.Add(StructureParameter(model));
                                            else
                                                RedisQueueHelper.ListPush(RedisQueueHelperParameter.SingleNull, item);
                                        }
                                        else
                                            RedisQueueHelper.ListPush(RedisQueueHelperParameter.SingleNull, item);
                                    }
                                }
                                else
                                    RedisQueueHelper.ListPush(RedisQueueHelperParameter.SingleNull, item);
                            }

                            // 发送客户端
                            WebSocketApplication.SendAsync(webSocket, new { type = "offline", date = offline });

                            #endregion
                        });

                        client.ClientToken = token;

                        // 服务器标识
                        client.ServiceToken = Md5Helper.Hash(token + token);

                        return new Tuple<ClientInfo, bool>(client, true);
                    }
                }

                return new Tuple<ClientInfo, bool>(null, false);
            };

            /// <summary>
            /// 用户登出
            /// </summary>
            private Action<WebSocket> WsOutLogin = (ws) =>
            {
                // 用户登出
                if (WebSocketApplication.ClientsPool.ContainsKey(ws))
                {
                    WebSocketApplication.ClientsPool.TryRemove(ws, out ClientInfo modelClientInfo);
                    ws.Dispose();
                    RunningLoger.Info($"检查在线状态移除(WsOutLogin):当前状态为{ws.State}，{modelClientInfo.TryToJson()}");
                    OutClientFlag = true;
                }
            };

            /// <summary>
            /// 更新 服务集合
            /// </summary>
            void InitServiceClinetParameter(int Sleep)
            {
                while (true)
                {
                    Thread.Sleep(Sleep);
                    try
                    {
                        if (WebSocketApplication.ClientsPool.Count() > 0 || OutClientFlag)
                        {
                            OutClientFlag = false;
                            UpdateClientsSubscription();
                        }
                    }
                    catch (Exception e)
                    {
                        RunningLoger.Error($"InitServiceClinetParameter:{e.Message}");
                    }
                }
                
            }

            void UpdateClientsSubscription()
            {
                List<Listener_Template> temps = new List<Listener_Template>();

                foreach (var handler in TaskHandleFactory.MQMsgHandlers)
                {
                    var list = WebSocketApplication.ClientsPool
                        .Where(a => a.Key.State == WebSocketState.Open
                                    &&
                                    (
                                        a.Value.Template.ToLower() == handler.Template.ToLower()
                                        || ("WebSocket".ToLower() == handler.Template.ToLower() &&
                                            string.IsNullOrWhiteSpace(a.Value.Template))
                                    )
                        )
                        .Select(a => new
                        {
                            Subscription = a.Value.User.Subscription.ToList()
                        });

                    List<string> Subscription = new List<string>();

                    foreach (var item in list)
                        Subscription = Subscription.Union(item.Subscription).ToList();

                    temps.Add(new Listener_Template
                    {
                        template = handler.Template,
                        Subscription = Subscription
                    });
                }

                lock (lock_Subscription)
                {
                    var redis = new RedisHelper();
                    redis.HashSet(
                        RedisQueueHelperParameter.QueueService,
                        RedisQueueHelperParameter.ServiceClinet, new QueueService
                        {
                            Title = CoreStartupHelper.GetConfigValue("Service:Title"),
                            ApiUrl = CoreStartupHelper.GetConfigValue("Service:Url"),
                            Temps = temps,
                            System = CoreStartupHelper.GetConfigValue("Service:System"),
                            EndRefreshTime = DateTime.Now
                        });
                }
            }

        }

    }

}
